<template>
  <div class="base-upload">
    <n-upload
      :custom-request="afterRead"
      :key="renderKey"
      @remove="onRemove"
      v-bind="$attrs"
      v-model:file-list="fileList"
    >
      <slot>点击上传</slot>
    </n-upload>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'
import { formatDateTime, getUUID } from '@/utils'

const props = defineProps(['value', 'maxSize', 'isPreview'])
const emit = defineEmits(['update:value', 'increment', 'decrement'])

const fileList = ref([])
const renderKey = ref(0)

watch(
  () => props.value,
  () => {
    if (props.isPreview || typeof props.isPreview === 'undefined') {
      reviewFileList()
    }
  },
  {
    immediate: true,
  }
)

// 上传图片
async function afterRead({ file, onFinish, onError, onProgress }) {
  try {
    // 大小限制
    if (props.maxSize) {
      const size = file.file.size / 1024 / 1024

      if (size > props.maxSize) {
        fileList.value.splice(fileList.value.length - 1, 1)
        onError()
        return $message.error('超出大小限制，请重新上传。')
      }
    }

    emit('increment')

    onProgress({ percent: 99 })
    const fileName = file.name
    const fileType = fileName.slice(fileName.lastIndexOf('.'))
    const compress = props.maxSize ? null : await compressFile(file.file)
    const blob = props.maxSize ? null : dataURLtoBlob(compress)
    const fileData = props.maxSize ? file.file : blobToFile(blob, file.name)

    const { url } = await window.ossClient.put(
      `wenjuan/${formatDateTime(
        new Date(),
        'YYYYMMDD'
      )}/${getUUID()}${fileType}`,
      fileData
    )

    // const url =
    // 'https://uii-bucket.oss-cn-shanghai.aliyuncs.com/wenjuan/9b2fce2e-b0db-4086-8f7b-4f8db06cd384.jpg'

    fileList.value[fileList.value.length - 1].url = url

    emit('decrement')
    update()
    onFinish()
  } catch (err) {
    console.log(err)
    fileList.value.splice(fileList.value.length - 1, 1)
    emit('decrement')
    onError()
    $message.error('上传失败，请重试')
  }
}

// 压缩图片
function compressFile(file) {
  return new Promise((resolve, reject) => {
    try {
      const reader = new FileReader()

      reader.onload = (e) => {
        const img = new Image()
        img.src = e.target.result

        img.onload = () => {
          const canvas = document.createElement('canvas')
          const ctx = canvas.getContext('2d')
          canvas.width = img.width
          canvas.height = img.height
          ctx.drawImage(img, 0, 0, img.width, img.height)

          const base64 = canvas.toDataURL('image/jpeg', 0.2)
          resolve(base64)
        }
      }

      reader.readAsDataURL(file)
    } catch (err) {
      reject(err)
    }
  })
}

// 删除图片
function onRemove() {
  setTimeout(() => {
    update()
  }, 0)
}

// 回显图片
function reviewFileList() {
  const list = props.value || []

  fileList.value = list
    .filter((url) => url)
    .map((url) => {
      return {
        status: 'finished',
        url,
        name: url.slice(url.lastIndexOf('/') + 1),
      }
    })

  renderKey.value += 1
}

// 双向绑定
function update() {
  emit(
    'update:value',
    fileList.value.map((v) => v.url)
  )
}

function dataURLtoBlob(dataurl) {
  var arr = dataurl.split(','),
    mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]),
    n = bstr.length,
    u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new Blob([u8arr], { type: mime })
}

function blobToFile(theBlob, fileName) {
  theBlob.lastModifiedDate = new Date()
  theBlob.name = fileName

  return new File([theBlob], fileName, {
    type: theBlob.type,
    lastModified: Date.now(),
  })
}

defineExpose({})
</script>

<style lang="scss" scoped>
.base-upload {
  :deep(.n-upload) {
    .n-upload-trigger {
      width: 100%;
    }
  }
}
</style>
